prevent infinite sync loop: delay retry after many errors
authorMatthieu Gallien <matthieu.gallien@nextcloud.com>
Mon, 17 Feb 2025 17:52:54 +0000 (18:52 +0100)
committerbackportbot[bot] <backportbot[bot]@users.noreply.github.com>
Thu, 20 Feb 2025 09:09:56 +0000 (09:09 +0000)
10 seconds for try 3 and 4
30 seconds for try 5 and 6
60 seconds for more

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
src/gui/folderman.cpp
test/testfolderman.cpp

index 6f84af9ad4f108303af1b4eba7f174e3791e400c..c9b7faaea910bb21304062ad4506c51e216b67b8 100644 (file)
@@ -719,21 +719,49 @@ void FolderMan::scheduleFolder(Folder *f)
 
     qCInfo(lcFolderMan) << "Schedule folder " << alias << " to sync!";
 
+    auto syncAgainDelay = std::chrono::seconds(0);
+    if (f->consecutiveFailingSyncs() > 2 && f->consecutiveFailingSyncs() <= 4) {
+        syncAgainDelay = std::chrono::seconds(10);
+    } else if (f->consecutiveFailingSyncs() > 4 && f->consecutiveFailingSyncs() <= 6) {
+        syncAgainDelay = std::chrono::seconds(30);
+    } else if (f->consecutiveFailingSyncs() > 6) {
+        syncAgainDelay = std::chrono::seconds(60);
+    }
+
     if (!_scheduledFolders.contains(f)) {
         if (!f->canSync()) {
             qCInfo(lcFolderMan) << "Folder is not ready to sync, not scheduled!";
             _socketApi->slotUpdateFolderView(f);
             return;
         }
-        f->prepareToSync();
-        emit folderSyncStateChange(f);
-        _scheduledFolders.enqueue(f);
-        emit scheduleQueueChanged();
+
+        if (syncAgainDelay == std::chrono::seconds(0)) {
+            f->prepareToSync();
+            emit folderSyncStateChange(f);
+            _scheduledFolders.enqueue(f);
+            emit scheduleQueueChanged();
+            startScheduledSyncSoon();
+        } else {
+            qCWarning(lcFolderMan()) << "going to delay the next sync run due to too many synchronization errors" << syncAgainDelay;
+            QTimer::singleShot(syncAgainDelay, this, [this, f] () {
+                f->prepareToSync();
+                emit folderSyncStateChange(f);
+                _scheduledFolders.enqueue(f);
+                emit scheduleQueueChanged();
+                startScheduledSyncSoon();
+            });
+        }
     } else {
         qCInfo(lcFolderMan) << "Sync for folder " << alias << " already scheduled, do not enqueue!";
+        if (syncAgainDelay == std::chrono::seconds(0)) {
+            startScheduledSyncSoon();
+        } else {
+            qCWarning(lcFolderMan()) << "going to delay the next sync run due to too many synchronization errors" << syncAgainDelay;
+            QTimer::singleShot(syncAgainDelay, this, [this] () {
+                startScheduledSyncSoon();
+            });
+        }
     }
-
-    startScheduledSyncSoon();
 }
 
 void FolderMan::scheduleFolderForImmediateSync(Folder *f)
index c837378281c2470f7e5fae17c93370f47608b95e..fa01cb51a869e0b641917b2c607218e9fd7205c9 100644 (file)
@@ -37,7 +37,7 @@ class TestFolderMan: public QObject
 {
     Q_OBJECT
 
-    FolderMan _fm;
+    std::unique_ptr<FolderMan> _fm;
 
 signals:
     void incomingShareDeleted();
@@ -53,6 +53,9 @@ private slots:
 
     void testDeleteEncryptedFiles()
     {
+        _fm.reset({});
+        _fm.reset(new FolderMan{});
+
         FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()};
         QCOMPARE(fakeFolder.currentLocalState().children.count(), 4);
 
@@ -154,6 +157,9 @@ private slots:
 
     void testLeaveShare()
     {
+        _fm.reset({});
+        _fm.reset(new FolderMan{});
+
         QTemporaryDir dir;
         ConfigFile::setConfDir(dir.path()); // we don't want to pollute the user's config file
 
@@ -182,7 +188,7 @@ private slots:
         secondShare->permissions.setPermission(OCC::RemotePermissions::IsShared);
 
         FolderMan *folderman = FolderMan::instance();
-        QCOMPARE(folderman, &_fm);
+        QCOMPARE(folderman, _fm.get());
         OCC::AccountState *accountState = OCC::AccountManager::instance()->accounts().first().data();
         const auto folder = folderman->addFolder(accountState, folderDefinition(fakeFolder.localPath()));
         QVERIFY(folder);
@@ -252,6 +258,9 @@ private slots:
 
     void testCheckPathValidityForNewFolder()
     {
+        _fm.reset({});
+        _fm.reset(new FolderMan{});
+
 #ifdef Q_OS_WIN
         Utility::NtfsPermissionLookupRAII ntfs_perm;
 #endif
@@ -278,7 +287,7 @@ private slots:
 
         AccountStatePtr newAccountState(new AccountState(account));
         FolderMan *folderman = FolderMan::instance();
-        QCOMPARE(folderman, &_fm);
+        QCOMPARE(folderman, _fm.get());
         QVERIFY(folderman->addFolder(newAccountState.data(), folderDefinition(dirPath + "/sub/ownCloud1")));
         QVERIFY(folderman->addFolder(newAccountState.data(), folderDefinition(dirPath + "/ownCloud2")));
 
@@ -382,6 +391,9 @@ private slots:
 
     void testFindGoodPathForNewSyncFolder()
     {
+        _fm.reset({});
+        _fm.reset(new FolderMan{});
+
         // SETUP
 
         QTemporaryDir dir;
@@ -405,7 +417,7 @@ private slots:
 
         AccountStatePtr newAccountState(new AccountState(account));
         FolderMan *folderman = FolderMan::instance();
-        QCOMPARE(folderman, &_fm);
+        QCOMPARE(folderman, _fm.get());
         QVERIFY(folderman->addFolder(newAccountState.data(), folderDefinition(dirPath + "/sub/ownCloud/")));
         QVERIFY(folderman->addFolder(newAccountState.data(), folderDefinition(dirPath + "/ownCloud2/")));